/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.plc4x.java.cbus.readwrite;

import static org.apache.plc4x.java.spi.codegen.fields.FieldReaderFactory.*;
import static org.apache.plc4x.java.spi.codegen.fields.FieldWriterFactory.*;
import static org.apache.plc4x.java.spi.codegen.io.DataReaderFactory.*;
import static org.apache.plc4x.java.spi.codegen.io.DataWriterFactory.*;
import static org.apache.plc4x.java.spi.generation.StaticHelper.*;

import java.time.*;
import java.util.*;
import org.apache.plc4x.java.api.exceptions.*;
import org.apache.plc4x.java.api.value.*;
import org.apache.plc4x.java.spi.codegen.*;
import org.apache.plc4x.java.spi.codegen.fields.*;
import org.apache.plc4x.java.spi.codegen.io.*;
import org.apache.plc4x.java.spi.generation.*;

// Code generated by code-generation. DO NOT EDIT.

public abstract class LevelInformation implements Message {

  // Abstract accessors for discriminator values.

  // Properties.
  protected final int raw;

  public LevelInformation(int raw) {
    super();
    this.raw = raw;
  }

  public int getRaw() {
    return raw;
  }

  public byte getNibble1() {
    return (byte) ((((getRaw()) & (0xF000))) >> (12));
  }

  public byte getNibble2() {
    return (byte) ((((getRaw()) & (0x0F00))) >> (8));
  }

  public byte getNibble3() {
    return (byte) ((((getRaw()) & (0x00F0))) >> (4));
  }

  public byte getNibble4() {
    return (byte) ((((getRaw()) & (0x000F))) >> (0));
  }

  public boolean getIsAbsent() {
    return (boolean)
        (((((getNibble1()) == (0x0)) && ((getNibble2()) == (0x0))) && ((getNibble3()) == (0x0)))
            && ((getNibble4()) == (0x0)));
  }

  public boolean getIsCorruptedByNoise() {
    return (boolean)
        ((!(getIsAbsent()))
            && ((((((((((getNibble1()) < (0x5))) || (((getNibble1()) == (0x8))))
                            || (((getNibble1()) == (0xC)))))
                        || ((((((getNibble2()) < (0x5))) || (((getNibble2()) == (0x8))))
                            || (((getNibble2()) == (0xC))))))
                    || ((((((getNibble3()) < (0x5))) || (((getNibble3()) == (0x8))))
                        || (((getNibble3()) == (0xC))))))
                || ((((((getNibble4()) < (0x5))) || (((getNibble4()) == (0x8))))
                    || (((getNibble4()) == (0xC))))))));
  }

  public boolean getIsCorruptedByNoiseOrLevelsDiffer() {
    return (boolean)
        ((!(getIsAbsent()))
            && ((((((((((getNibble1()) == (0x7))) || (((getNibble1()) == (0xB))))
                            || (((getNibble1()) > (0xC)))))
                        || ((((((getNibble2()) == (0x7))) || (((getNibble2()) == (0xB))))
                            || (((getNibble2()) > (0xC))))))
                    || ((((((getNibble3()) == (0x7))) || (((getNibble3()) == (0xB))))
                        || (((getNibble3()) > (0xC))))))
                || ((((((getNibble4()) == (0x7))) || (((getNibble4()) == (0xB))))
                    || (((getNibble4()) > (0xC))))))));
  }

  public boolean getIsCorrupted() {
    return (boolean) ((getIsCorruptedByNoise()) || (getIsCorruptedByNoiseOrLevelsDiffer()));
  }

  protected abstract void serializeLevelInformationChild(WriteBuffer writeBuffer)
      throws SerializationException;

  public void serialize(WriteBuffer writeBuffer) throws SerializationException {
    PositionAware positionAware = writeBuffer;
    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();
    writeBuffer.pushContext("LevelInformation");

    // Virtual field (doesn't serialize anything, just makes the value available)
    byte nibble1 = getNibble1();
    writeBuffer.writeVirtual("nibble1", nibble1);

    // Virtual field (doesn't serialize anything, just makes the value available)
    byte nibble2 = getNibble2();
    writeBuffer.writeVirtual("nibble2", nibble2);

    // Virtual field (doesn't serialize anything, just makes the value available)
    byte nibble3 = getNibble3();
    writeBuffer.writeVirtual("nibble3", nibble3);

    // Virtual field (doesn't serialize anything, just makes the value available)
    byte nibble4 = getNibble4();
    writeBuffer.writeVirtual("nibble4", nibble4);

    // Virtual field (doesn't serialize anything, just makes the value available)
    boolean isAbsent = getIsAbsent();
    writeBuffer.writeVirtual("isAbsent", isAbsent);

    // Virtual field (doesn't serialize anything, just makes the value available)
    boolean isCorruptedByNoise = getIsCorruptedByNoise();
    writeBuffer.writeVirtual("isCorruptedByNoise", isCorruptedByNoise);

    // Virtual field (doesn't serialize anything, just makes the value available)
    boolean isCorruptedByNoiseOrLevelsDiffer = getIsCorruptedByNoiseOrLevelsDiffer();
    writeBuffer.writeVirtual("isCorruptedByNoiseOrLevelsDiffer", isCorruptedByNoiseOrLevelsDiffer);

    // Virtual field (doesn't serialize anything, just makes the value available)
    boolean isCorrupted = getIsCorrupted();
    writeBuffer.writeVirtual("isCorrupted", isCorrupted);

    // Switch field (Serialize the sub-type)
    serializeLevelInformationChild(writeBuffer);

    writeBuffer.popContext("LevelInformation");
  }

  @Override
  public int getLengthInBytes() {
    return (int) Math.ceil((float) getLengthInBits() / 8.0);
  }

  @Override
  public int getLengthInBits() {
    int lengthInBits = 0;
    LevelInformation _value = this;
    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();

    // A virtual field doesn't have any in- or output.

    // A virtual field doesn't have any in- or output.

    // A virtual field doesn't have any in- or output.

    // A virtual field doesn't have any in- or output.

    // A virtual field doesn't have any in- or output.

    // A virtual field doesn't have any in- or output.

    // A virtual field doesn't have any in- or output.

    // A virtual field doesn't have any in- or output.

    // Length of sub-type elements will be added by sub-type...

    return lengthInBits;
  }

  public static LevelInformation staticParse(ReadBuffer readBuffer) throws ParseException {
    readBuffer.pullContext("LevelInformation");
    PositionAware positionAware = readBuffer;
    boolean _lastItem = ThreadLocalHelper.lastItemThreadLocal.get();

    int raw = readPeekField("raw", readUnsignedInt(readBuffer, 16));
    byte nibble1 = readVirtualField("nibble1", byte.class, (((raw) & (0xF000))) >> (12));
    byte nibble2 = readVirtualField("nibble2", byte.class, (((raw) & (0x0F00))) >> (8));
    byte nibble3 = readVirtualField("nibble3", byte.class, (((raw) & (0x00F0))) >> (4));
    byte nibble4 = readVirtualField("nibble4", byte.class, (((raw) & (0x000F))) >> (0));
    boolean isAbsent =
        readVirtualField(
            "isAbsent",
            boolean.class,
            ((((nibble1) == (0x0)) && ((nibble2) == (0x0))) && ((nibble3) == (0x0)))
                && ((nibble4) == (0x0)));
    boolean isCorruptedByNoise =
        readVirtualField(
            "isCorruptedByNoise",
            boolean.class,
            (!(isAbsent))
                && ((((((((((nibble1) < (0x5))) || (((nibble1) == (0x8))))
                                || (((nibble1) == (0xC)))))
                            || ((((((nibble2) < (0x5))) || (((nibble2) == (0x8))))
                                || (((nibble2) == (0xC))))))
                        || ((((((nibble3) < (0x5))) || (((nibble3) == (0x8))))
                            || (((nibble3) == (0xC))))))
                    || ((((((nibble4) < (0x5))) || (((nibble4) == (0x8))))
                        || (((nibble4) == (0xC))))))));
    boolean isCorruptedByNoiseOrLevelsDiffer =
        readVirtualField(
            "isCorruptedByNoiseOrLevelsDiffer",
            boolean.class,
            (!(isAbsent))
                && ((((((((((nibble1) == (0x7))) || (((nibble1) == (0xB))))
                                || (((nibble1) > (0xC)))))
                            || ((((((nibble2) == (0x7))) || (((nibble2) == (0xB))))
                                || (((nibble2) > (0xC))))))
                        || ((((((nibble3) == (0x7))) || (((nibble3) == (0xB))))
                            || (((nibble3) > (0xC))))))
                    || ((((((nibble4) == (0x7))) || (((nibble4) == (0xB))))
                        || (((nibble4) > (0xC))))))));
    boolean isCorrupted =
        readVirtualField(
            "isCorrupted",
            boolean.class,
            (isCorruptedByNoise) || (isCorruptedByNoiseOrLevelsDiffer));

    // Switch Field (Depending on the discriminator values, passes the instantiation to a sub-type)
    LevelInformationBuilder builder = null;
    if (EvaluationHelper.equals(isAbsent, (boolean) true)) {
      builder = LevelInformationAbsent.staticParseLevelInformationBuilder(readBuffer);
    } else if (true && EvaluationHelper.equals(isCorrupted, (boolean) true)) {
      builder = LevelInformationCorrupted.staticParseLevelInformationBuilder(readBuffer);
    } else if (true) {
      builder = LevelInformationNormal.staticParseLevelInformationBuilder(readBuffer);
    }
    if (builder == null) {
      throw new ParseException(
          "Unsupported case for discriminated type"
              + " parameters ["
              + "isAbsent="
              + isAbsent
              + " "
              + "isCorrupted="
              + isCorrupted
              + "]");
    }

    readBuffer.closeContext("LevelInformation");
    // Create the instance
    LevelInformation _levelInformation = builder.build(raw);
    return _levelInformation;
  }

  public interface LevelInformationBuilder {
    LevelInformation build(int raw);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!(o instanceof LevelInformation)) {
      return false;
    }
    LevelInformation that = (LevelInformation) o;
    return (getRaw() == that.getRaw()) && true;
  }

  @Override
  public int hashCode() {
    return Objects.hash(getRaw());
  }

  @Override
  public String toString() {
    WriteBufferBoxBased writeBufferBoxBased = new WriteBufferBoxBased(true, true);
    try {
      writeBufferBoxBased.writeSerializable(this);
    } catch (SerializationException e) {
      throw new RuntimeException(e);
    }
    return "\n" + writeBufferBoxBased.getBox().toString() + "\n";
  }
}
